using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Reflection;

namespace Xnlab.Filio
{
#if (NTFS)
    #region NTFS
    internal class NTFSEntry : ICustomBinarySerializable
    {
        public string Drive;
        public UInt64 UsnJournalID;
        public Int64 FirstUsn;
        public Int64 NextUsn;
        public Int64 LowestValidUsn;
        public Int64 MaxUsn;
        public UInt64 MaximumSize;
        public UInt64 AllocationDelta;

        internal NTFSEntry(string Drive, UInt64 UsnJournalID, Int64 FirstUsn, Int64 NextUsn, Int64 LowestValidUsn, Int64 MaxUsn, UInt64 MaximumSize, UInt64 AllocationDelta)
        {
            this.Drive = Drive;
            this.UsnJournalID = UsnJournalID;
            this.FirstUsn = FirstUsn;
            this.NextUsn = NextUsn;
            this.LowestValidUsn = LowestValidUsn;
            this.MaxUsn = MaxUsn;
            this.MaximumSize = MaximumSize;
            this.AllocationDelta = AllocationDelta;
        }

        public void WriteDataTo(BinaryWriter writer)
        {
            writer.Write(Drive);
            writer.Write(UsnJournalID);
            writer.Write(FirstUsn);
            writer.Write(NextUsn);
            writer.Write(LowestValidUsn);
            writer.Write(MaxUsn);
            writer.Write(MaximumSize);
            writer.Write(AllocationDelta);
        }

        public void SetDataFrom(BinaryReader reader)
        {
            Drive = reader.ReadString();
            UsnJournalID = reader.ReadUInt64();
            FirstUsn = reader.ReadInt64();
            NextUsn = reader.ReadInt64();
            LowestValidUsn = reader.ReadInt64();
            MaxUsn = reader.ReadInt64();
            MaximumSize = reader.ReadUInt64();
            AllocationDelta = reader.ReadUInt64();
        }
    }

    internal class ItemInfo : ICustomBinarySerializable
    {
        public string Name;
        public bool State;

        internal ItemInfo(string Name, bool Flag)
        {
            this.Name = Name;
            this.State = Flag;
        }

        public void WriteDataTo(BinaryWriter writer)
        {
            writer.Write(Name);
            writer.Write(State);
        }

        public void SetDataFrom(BinaryReader reader)
        {
            Name = reader.ReadString();
            State = reader.ReadBoolean();
        }
    }

    internal class NTFSInfo : ICustomBinarySerializable
    {
        public int Count;

        internal NTFSInfo(int Count)
        {
            this.Count = Count;
        }

        public void WriteDataTo(BinaryWriter writer)
        {
            writer.Write(Count);
        }

        public void SetDataFrom(BinaryReader reader)
        {
            Count = reader.ReadInt32();
        }
    }

    internal class LocalNTFSStorage
    {
        private const int HeaderSize = 100;
        private const int MaxTryTimes = 20;
        private static readonly object _syncRoot = new object();
        private static int count = 0;
        private MemoryStream stream = null;
        private MemoryStream idx = null;
        private CustomBinaryFormatter formatter = null;
        private const string fileName = "NTFSUSNJournal";
        private string dataFile = string.Empty;
        private string indexFile = string.Empty;

        private static LocalNTFSStorage storage = null;
        internal static LocalNTFSStorage Instance
        {
            get
            {
                lock (_syncRoot)
                {
                    if (storage == null)
                        storage = new LocalNTFSStorage();
                    return storage;
                }
            }
        }

        internal LocalNTFSStorage()
        {
            OpenStorage();

            formatter = new CustomBinaryFormatter(idx, stream);
            formatter.Register<NTFSInfo>(1);
            formatter.Register<NTFSEntry>(2);
            formatter.Register<ItemInfo>(3);
        }

        private void OpenStorage()
        {
            try
            {
                count++;
                dataFile = WorkUtils.ApplicationPath + fileName + count + ".dbc";
                stream = new MemoryStream();
                byte[] buffer;
                if (File.Exists(dataFile) && new FileInfo(dataFile).Length > 0)
                {
                    buffer = File.ReadAllBytes(dataFile);
                    buffer = WorkUtils.DecompressBuffer(buffer);
                    stream.Write(buffer, 0, buffer.Length);
                }

                indexFile = Path.ChangeExtension(dataFile, ".idc");
                idx = new MemoryStream();
                if (File.Exists(indexFile) && new FileInfo(indexFile).Length > 0)
                {
                    buffer = File.ReadAllBytes(indexFile);
                    buffer = WorkUtils.DecompressBuffer(buffer);
                    idx.Write(buffer, 0, buffer.Length);
                    idx.Position = 0;
                }
            }
            catch (Exception)
            {
                if (count < MaxTryTimes)
                    OpenStorage();
            }
        }

        ~LocalNTFSStorage()
        {
            if (formatter != null)
                formatter.Close();
        }

        private void AddFiles(string Drive, NtfsUsnJournal UsnJournal, List<Win32Api.UsnEntry> Files)
        {
            Dictionary<ulong, string> parentNames = new Dictionary<ulong, string>();
            foreach (Win32Api.UsnEntry item in Files)
            {
                if (!parentNames.ContainsKey(item.ParentFileReferenceNumber))
                {
                    string parent = string.Empty;
                    if (item.ParentFileReferenceNumber != 0)
                        UsnJournal.GetPathFromFileReference(item.ParentFileReferenceNumber, out parent);
                    parentNames.Add(item.ParentFileReferenceNumber, parent);
                }
            }
            formatter.MoveToEnd();
            foreach (Win32Api.UsnEntry item in Files)
            {
                bool state;
                switch (item.Reason)
                {
                    case Win32Api.USN_REASON_FILE_DELETE:
                        state = false;
                        break;
                    case Win32Api.USN_REASON_FILE_CREATE:
                        state = true;
                        break;
                    case Win32Api.USN_REASON_RENAME_NEW_NAME:
                        state = true;
                        break;
                    case Win32Api.USN_REASON_RENAME_OLD_NAME:
                        state = false;
                        break;
                    case Win32Api.USN_REASON_DATA_OVERWRITE:
                        state = true;
                        break;
                    case Win32Api.USN_REASON_DATA_ADDED:
                        state = true;
                        break;
                    case Win32Api.USN_REASON_DATA_SHORTENED:
                        state = true;
                        break;
                    default:
                        state = true;
                        break;
                }
                string parent = parentNames[item.ParentFileReferenceNumber];
                if (string.IsNullOrEmpty(parent))
                    parent = @"\";
                else
                    parent = WorkUtils.GetFullPath(parent);
                formatter.Serialize<ItemInfo>(new ItemInfo(Drive + parent + item.Name, state));
            }
            Flush(false);
        }

        public void Flush()
        {
            Flush(true);
        }

        private void Flush(bool Save)
        {
            formatter.Flush();
            if (Save)
            {
                byte[] buffer = ((MemoryStream)formatter.IndexStream).ToArray();
                buffer = WorkUtils.CompressBuffer(buffer);
                File.WriteAllBytes(indexFile, buffer);

                buffer = ((MemoryStream)formatter.DataStream).ToArray();
                buffer = WorkUtils.CompressBuffer(buffer);
                File.WriteAllBytes(dataFile, buffer);
            }
        }

        private void AddDrive(string Drive, NtfsUsnJournal usnJournal)
        {
            List<Win32Api.UsnEntry> usnFolders;
            List<Win32Api.UsnEntry> usnFiles;
            NtfsUsnJournal.UsnJournalReturnCode state = usnJournal.GetNtfsVolumeFolders(out usnFolders, out usnFiles);
            AddFiles(Drive, usnJournal, usnFiles);
        }

        internal string[] GetFiles(string Drive)
        {
            try
            {
                Drive = Drive.ToLower().TrimEnd('\\');
                DriveInfo drive = new DriveInfo(Drive);
                using (NtfsUsnJournal usnJournal = new NtfsUsnJournal(drive))
                {
                    NtfsUsnJournal.UsnJournalReturnCode state;
                    bool isNew;
                    if (formatter.Count == 0)
                    {
                        formatter.Serialize<NTFSInfo>(new NTFSInfo(1));
                        for (int i = 0; i < HeaderSize - 1; i++)
                        {
                            formatter.Serialize<NTFSEntry>(new NTFSEntry(Drive, 0, 0, 0, 0, 0, 0, 0));
                        }
                        AddDrive(Drive, usnJournal);
                        isNew = false;
                    }
                    else
                        isNew = true;

                    if (formatter.Count > 0)
                    {
                        Win32Api.USN_JOURNAL_DATA journalState = new Win32Api.USN_JOURNAL_DATA();
                        //state = usnJournal.GetUsnJournalState(ref journalState);

                        formatter.MoveTo(0);
                        NTFSInfo ntfsInfo = formatter.Deserialize<NTFSInfo>();
                        int index = 0;
                        bool found = false;
                        for (int i = 0; i < ntfsInfo.Count; i++)
                        {
                            NTFSEntry entry = formatter.Deserialize<NTFSEntry>();
                            if (entry.Drive == Drive)
                            {
                                index = i + 1;
                                found = true;
                                break;
                            }
                        }
                        if (index == 0)
                        {
                            index = ntfsInfo.Count + 1;
                        }
                        //SaveState(Drive, index, journalState);
                        if (!found)
                        {
                            formatter.MoveTo(0);
                            formatter.Serialize<NTFSInfo>(new NTFSInfo(ntfsInfo.Count + 1));
                            AddDrive(Drive, usnJournal);
                        }

                        if (isNew)
                        {
                            if (index > 0 && index < formatter.Count)
                            {
                                formatter.MoveTo(index);
                                NTFSEntry ntfsEntry = formatter.Deserialize<NTFSEntry>();
                                journalState.AllocationDelta = ntfsEntry.AllocationDelta;
                                journalState.FirstUsn = ntfsEntry.FirstUsn;
                                journalState.LowestValidUsn = ntfsEntry.LowestValidUsn;
                                journalState.MaximumSize = ntfsEntry.MaximumSize;
                                journalState.MaxUsn = ntfsEntry.MaxUsn;
                                journalState.NextUsn = ntfsEntry.NextUsn;
                                journalState.UsnJournalID = ntfsEntry.UsnJournalID;
                                uint reasonMask = Win32Api.USN_REASON_DATA_OVERWRITE |
                                        Win32Api.USN_REASON_DATA_EXTEND |
                                        Win32Api.USN_REASON_NAMED_DATA_OVERWRITE |
                                        Win32Api.USN_REASON_NAMED_DATA_TRUNCATION |
                                        Win32Api.USN_REASON_FILE_CREATE |
                                        Win32Api.USN_REASON_FILE_DELETE |
                                        Win32Api.USN_REASON_EA_CHANGE |
                                    //Win32Api.USN_REASON_SECURITY_CHANGE |
                                        Win32Api.USN_REASON_RENAME_OLD_NAME |
                                        Win32Api.USN_REASON_RENAME_NEW_NAME |
                                        Win32Api.USN_REASON_INDEXABLE_CHANGE |
                                        Win32Api.USN_REASON_BASIC_INFO_CHANGE |
                                        Win32Api.USN_REASON_HARD_LINK_CHANGE |
                                        Win32Api.USN_REASON_COMPRESSION_CHANGE |
                                        Win32Api.USN_REASON_ENCRYPTION_CHANGE |
                                    //Win32Api.USN_REASON_OBJECT_ID_CHANGE |
                                    //Win32Api.USN_REASON_REPARSE_POINT_CHANGE |
                                        Win32Api.USN_REASON_STREAM_CHANGE; //|
                                //Win32Api.USN_REASON_CLOSE;

                                Win32Api.USN_JOURNAL_DATA newUsnState;
                                List<Win32Api.UsnEntry> usnEntries;
                                state = usnJournal.GetUsnJournalEntries(journalState, reasonMask, out usnEntries, out newUsnState);
                                SaveState(Drive, index, newUsnState);
                                if (usnEntries.Count > 0)
                                    AddFiles(Drive, usnJournal, usnEntries);
                                else
                                    Flush(false);
                            }
                        }
                    }

                    if (formatter.Count > HeaderSize)
                    {
                        List<string> result = new List<string>();

                        formatter.MoveTo(HeaderSize);
                        ItemInfo item;
                        while ((item = formatter.Deserialize<ItemInfo>()) != null)
                        {
                            if (item.State)
                            {
                                if (item.Name.Substring(0, 2).ToLower() == Drive)
                                    result.Add(item.Name);
                            }
                            else
                            {
                                if (result.Contains(item.Name))
                                    result.Remove(item.Name);
                            }
                        }
                        return result.ToArray();
                    }
                    else
                        return null;
                }
            }
            catch (Exception ex)
            {
                WorkUtils.AddVerbose(ex.ToString());
                return null;
            }
        }

        private void SaveState(string Drive, int Index, Win32Api.USN_JOURNAL_DATA UsnState)
        {
            if (Index > 0)
            {
                long position = 0;
                if (Index < formatter.Count)
                {
                    formatter.MoveTo(Index, false);
                    formatter.IndexStream.Position = position;
                }
                else
                    formatter.MoveToEnd();
                formatter.Serialize<NTFSEntry>(new NTFSEntry(Drive, UsnState.UsnJournalID, UsnState.FirstUsn, UsnState.NextUsn, UsnState.LowestValidUsn, UsnState.MaxUsn, UsnState.MaximumSize, UsnState.AllocationDelta));
            }
        }
    }
    #endregion
#endif


    #region LocalStorage
    internal class DefaultFileContentIndexer : IFileContentIndex
    {
        public event EventHandler<FileProcessEventArgs> BeforeFileProcess;

        //http://www.microsoft.com/windows/products/winfamily/desktopsearch/technicalresources/filetypes.mspx
        private const string knownSearchableFileTypeList = "eml,msg,vcard,vcf,ics,vcs,ade,adp,mpd,mde,mpc,mpp,mpv,vdx,mpx,vsl,vst,vsw,vsx,vtx,ade,adp,mpd,mde,mpc,mpp,mpv,vdx,mpx,vsl,vst,vsw,vsx,vtx,dvi,eps,jnt,latex,pm,pm5,ps,pt5,rtx,tex,xml,pdf,doc,dot,htm,html,mht,one,rtf,txt,xml,csv,tsv,wks,xlb,xlc,xll,xlm,xls,xlw,ppz,pot,pps,ppt,xls,xlw,aac,aif,aiff,amf,au,cda,dfs,dss,far,m3u,mid,midi,mp3,mus,okt,p16,psm,ra,ram,rmi,s3m,snd,sng,stm,ult,uw,voc,wav,wma,xm,xmi,3ds,a11,ai,ani,anm,art,b_w,b&w,b1n,b8,bga,bit,bld,bm,cdf,cdr,cmp,cps,cvs,dib,dip,dcx,dkb,dw2,dwg,fh3,fh4,fit,flc,fli,gcd,gif,gl,gsd,hrf,hs2,hsi,iax,ica,jas,jff,jpc,icb,jpe,jpeg,jpg,jtf,lbm,mpt,msp,nc,neo,pct,pcx,pda,pdd,pgm,pix,png,ppm,psd,pse,qdv,rgb,rif,rip,rix,rl4,rl8,sg1,tif,tiff,van,vda,vdr,wmf,xif,xpm,asf,asx,avi,awm,cmv,m1v,mmm,mov,mp2,mpa,mpe,mpeg,mpg,mpv2,mwf,qt,vue,wmv,wvx,acf,aifc,ascx,asm,asp,aspx,bmp,cab,cs,css,cur,def,dic,emf,gz,hhc,ico,idq,ivf,ivf,jfif,lnk,mapiipm.Note,mda,mp2v,odc,pl,rle,scd,tar,tgz,tsp,wax,wbk,sch,wiz,wm,wmp,wmx,wmz,wri,wsz,wtx,xlk,z,zip,bat,c,cmd,cpp,cxx,Dif,disco,h,hpp,hxx,idl,inc,inf,inx,js,nws,pl,ppa,pwz,rc,reg,resx,slk,url,vbs,xla,xld,xlt,xlv,xsl";
        private static List<string> knownSearchableFileTypes = new List<string>();
        private static readonly object _syncRoot = new object();

        public bool Query(string Keyword, string FileName, out string Content)
        {
            if (knownSearchableFileTypes.Count == 0)
                knownSearchableFileTypes.AddRange(knownSearchableFileTypeList.Split(','));
            string extension = Path.GetExtension(FileName);
            if (!string.IsNullOrEmpty(extension))
                extension = extension.TrimStart('.').ToLower();
            if (knownSearchableFileTypes.Contains(extension))
            {
                bool result = LocalFileContentIndexStorage.Instance.Get(FileName, out Content);
                bool cancel;
                if (BeforeFileProcess != null)
                {
                    FileProcessEventArgs fpe = new FileProcessEventArgs(FileName);
                    BeforeFileProcess(this, fpe);
                    cancel = fpe.Cancel;
                }
                else
                    cancel = false;
                if (!cancel)
                {
                    if (!result)
                    {
                        try
                        {
                            lock (_syncRoot)
                            {
                                using (FilterReader filter = new FilterReader(FileName))
                                {
                                    Content = filter.ReadToEnd();
                                    filter.Close();
                                }
                            }
                        }
                        catch (Exception)
                        {
                            //mmm...infamous "already exists" exception...
                        }
                    }

                    try
                    {
                        //mmm...no relative filters...
                        if (string.IsNullOrEmpty(Content))
                        {
                            //if it's plain text
                            if (WorkUtils.MatchFileType(FileName, WorkUtils.DefaultFileTypes))
                            {
                                //and it has content
                                if (new FileInfo(FileName).Length > 0)
                                {
                                    //we just read it, quick & dirty, eh?
                                    Content = File.ReadAllText(FileName);
                                }
                            }
                        }
                        if (!result && !string.IsNullOrEmpty(Content))
                            LocalFileContentIndexStorage.Instance.Add(FileName, Content);
                        return Content.IndexOf(Keyword, StringComparison.CurrentCultureIgnoreCase) != -1;
                    }
                    catch (Exception)
                    {
                        return false;
                    }
                }
                else
                {
                    Content = string.Empty;
                    return false;
                }
            }
            else
            {
                Content = string.Empty;
                return false;
            }
        }
    }

    [Serializable]
    internal class FileStoreInfo : BasicFileInfo, ICustomBinarySerializable
    {
        private string content = string.Empty;
        private CompressionMethods compressionMethod;
        private int contentLength = 0;
        private byte[] contentBuffer = null;
        private bool contentReady = false;

        public FileStoreInfo()
        {

        }
        public FileStoreInfo(string FileName, long FileSize, DateTime LastModifiedDate, string Content, CompressionMethods CompressionMethod)
        {
            this.FileName = FileName;
            this.FileSize = FileSize;
            this.LastModifiedDate = LastModifiedDate;
            content = Content;
            compressionMethod = CompressionMethod;
        }

        public string Content
        {
            get
            {
                //dynamically decompress full text content, ref to workitem: http://filio.codeplex.com/workitem/28303
                if (compressionMethod != CompressionMethods.None && !contentReady)
                {
                    WorkUtils.Decompress(compressionMethod, ref contentBuffer);
                    content = Encoding.UTF8.GetString(contentBuffer);
                    contentReady = true;
                }
                return content;
            }
            set { content = value; }
        }

        public void WriteDataTo(BinaryWriter writer)
        {
            writer.Write(FileName);
            writer.Write(FileSize);
            writer.Write(LastModifiedDate.ToString());
            byte[] buffer = Encoding.UTF8.GetBytes(content);
            if (compressionMethod != CompressionMethods.None)
                WorkUtils.Compress(compressionMethod, ref buffer);
            writer.Write((int)compressionMethod);
            writer.Write(buffer.Length);
            writer.Write(buffer);
        }

        public void SetDataFrom(BinaryReader reader)
        {
            FileName = reader.ReadString();
            FileSize = reader.ReadInt64();
            string value = reader.ReadString();
            LastModifiedDate = Convert.ToDateTime(value);
            compressionMethod = (CompressionMethods)reader.ReadInt32();
            contentLength = reader.ReadInt32();
            contentBuffer = reader.ReadBytes(contentLength);
            if (compressionMethod == CompressionMethods.None)
                content = Encoding.UTF8.GetString(contentBuffer);
        }
    }

    internal class LocalFileContentIndexStorage : LocalStorage
    {
        private static readonly object _syncRoot = new object();
        private KeyValuePair<string, string> indexerInfo;
        private IFileContentIndex indexer = null;
        private bool initialized = false;

        private static LocalFileContentIndexStorage storage = null;
        internal static LocalFileContentIndexStorage Instance
        {
            get { return storage; }
        }

        internal LocalFileContentIndexStorage(KeyValuePair<string, string> IndexerInfo)
            : base("FullText", CompressionMethods.GZip)
        {
            lock (_syncRoot)
            {
                if (storage == null)
                    storage = this;
                indexerInfo = IndexerInfo;
            }
        }

        protected override void OnRequireProcess(StateDataEventArgs e)
        {
            if (!initialized && indexer == null)
            {
                if (!string.IsNullOrEmpty(indexerInfo.Key) && !string.IsNullOrEmpty(indexerInfo.Value))
                    indexer = DynamicInterface.GetInterface<IFileContentIndex>(indexerInfo.Key, indexerInfo.Value);
                else
                    indexer = new DefaultFileContentIndexer();
                initialized = true;
            }
            if (indexer != null)
            {
                string data;
                e.Result = indexer.Query(e.State, e.Content, out data);
                e.Response = data;
            }
        }
    }

    internal class LocalHashStorage : LocalStorage
    {
        private static readonly object _syncRoot = new object();

        private static LocalHashStorage storage = null;
        internal static LocalHashStorage Instance
        {
            get
            {
                lock (_syncRoot)
                {
                    if (storage == null)
                        storage = new LocalHashStorage();
                    return storage;
                }
            }
        }

        internal LocalHashStorage()
            : base("Storage", CompressionMethods.None)
        {
        }

        protected override void OnRequireProcess(StateDataEventArgs e)
        {
            string response;
            if (!Get(e.Content, out response))
            {
                if (!OnRequireFileProcess(e.Content))
                {
                    switch (WorkSettings.Instance.HashMethod)
                    {
                        case HashMethods.Block:
                            if (e.Length > WorkSettings.Instance.HashMinSize)
                                response = WorkUtils.HashMD5FileBlocks(e.Content);
                            else
                                response = WorkUtils.HashMD5File(e.Content);
                            break;
                        default:
                            response = WorkUtils.HashMD5File(e.Content);
                            break;
                    }
                    Add(e.Content, response);
                }
            }
            e.Response = response;
        }

    }

    internal class LocalStorage : IFileProcess, IDisposable
    {
        public event EventHandler<FileProcessEventArgs> BeforeFileProcess;
        internal event EventHandler<StateDataEventArgs> RequireProcess;
        private static readonly object _syncRoot = new object();
        private FileStream stream = null;
        private FileStream idx = null;
        private CustomBinaryFormatter formatter = null;
        private int count = 0;
        private string fileName = string.Empty;
        private CompressionMethods compressionMethod = CompressionMethods.None;

        internal LocalStorage(string FileName, CompressionMethods CompressionMethod)
        {
            fileName = FileName;
            compressionMethod = CompressionMethod;
            OpenStorage();

            formatter = new CustomBinaryFormatter(idx, stream);
            formatter.Register<FileStoreInfo>(1);
        }

        private void OpenStorage()
        {
            try
            {
                count++;
                string file = WorkUtils.ApplicationPath + fileName + count + ".db";
                string path = Path.GetDirectoryName(file);
                if (!Directory.Exists(path))
                    Directory.CreateDirectory(path);
                stream = new FileStream(file, FileMode.OpenOrCreate);
                idx = new FileStream(Path.ChangeExtension(file, ".idx"), FileMode.OpenOrCreate);
            }
            catch (Exception)
            {
                OpenStorage();
            }
        }

        ~LocalStorage()
        {
            Dispose();
        }

        internal void Flush()
        {
            formatter.Flush();
        }

        protected virtual void OnRequireProcess(StateDataEventArgs e)
        {
            if (RequireProcess != null)
                RequireProcess(this, e);
        }

        protected bool OnRequireFileProcess(string FileName)
        {
            if (BeforeFileProcess != null)
            {
                FileProcessEventArgs fpe = new FileProcessEventArgs(FileName);
                BeforeFileProcess(this, fpe);
                return fpe.Cancel;
            }
            else
                return false;
        }

        protected internal bool Query(string FilePath, long FileSize, string Keyword, out string Response)
        {
            StateDataEventArgs e = new StateDataEventArgs();
            e.Content = FilePath;
            e.State = Keyword;
            e.Length = FileSize;
            OnRequireProcess(e);
            Response = e.Response;
            return e.Result;
        }

        protected internal void Add(string FilePath, string Content)
        {
            FileInfo info = new FileInfo(FilePath);
            Add(FilePath, info.Length, info.LastWriteTime, Content);

        }

        protected internal void Add(string FilePath, long Size, DateTime ModifiedDate, string Content)
        {
            lock (_syncRoot)
            {
                FileStoreInfo store;
                store = new FileStoreInfo(FilePath.ToLower(), Size, ModifiedDate, Content, compressionMethod);
                formatter.Serialize(store);
            }
        }

        protected internal bool Get(string FilePath, out string Result)
        {
            FileInfo info = new FileInfo(FilePath);
            long size = info.Length;
            DateTime modifiedDate = info.LastWriteTime;
            return Get(FilePath, out Result, ref size, ref modifiedDate);
        }

        protected internal bool Get(string FilePath, out string Result, ref long Size, ref DateTime LastModifiedDate)
        {
            Result = string.Empty;
            bool found = false;
            try
            {
                lock (_syncRoot)
                {
                    if (formatter.Count > 0)
                    {
                        FileStoreInfo store;
                        string filePath = FilePath.ToLower();
                        for (int i = 0; i < formatter.Count; i++)
                        {
                            formatter.MoveTo(i);
                            store = formatter.Deserialize<FileStoreInfo>();
                            if (store != null)
                            {
                                if (store.FileName == filePath)
                                {
                                    if (Size >= 0 && LastModifiedDate > DateTime.MinValue)
                                    {
                                        if (store.FileSize == Size && store.LastModifiedDate.ToString() == LastModifiedDate.ToString())
                                        {
                                            Result = store.Content;
                                            found = true;
                                        }
                                        else
                                        {
                                            //file is changed
                                        }
                                    }
                                    else
                                    {
                                        Result = store.Content;
                                        found = true;
                                    }
                                    break;
                                }
                            }
                            else
                                break;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                //mmm....
                WorkUtils.AddVerbose(ex.ToString());
            }
            return found;
        }


        public void Dispose()
        {
            if (formatter != null)
                formatter.Close();
        }
    }
    #endregion
}
